import com.intellij.database.model.DasTable
import com.intellij.database.util.Case
import com.intellij.database.util.DasUtil

/*
 * Available context bindings:
 *   SELECTION   Iterable<DasObject>
 *   PROJECT     project
 *   FILES       files helper
 */
packageName = ""
hasId = false
hasBigDecimal = false
hasLocalDateTime = false
hasLocalDate = false
hasLocalTime = false

typeMapping = [
   (~/(?i)int/)                      : "long",
   (~/(?i)float|double|real/)        : "double",
   (~/(?i)decimal/)                 : "BigDecimal",
   (~/(?i)datetime|timestamp/)       : "java.time.LocalDateTime",
   (~/(?i)date/)                     : "java.time.LocalDate",
   (~/(?i)time/)                     : "java.time.LocalTime",
   (~/(?i)/)                         : "String"
]

FILES.chooseDirectoryAndSave("Choose directory", "Choose where to store generated files") { dir ->
   SELECTION.filter { it instanceof DasTable }.each { generate(it, dir) }
}

def generate(table, dir) {
   packageName = getPackageName(dir)
   def tableName = table.getName();
   def className = javaName(tableName.replaceAll("^T", ""), true) + "Entity"
   def fields = calcFields(table)
   new File(dir, className + ".java").withPrintWriter { out -> generate(out, className, tableName, fields) }
}

def generate(out, className, tableName, fields) {
   out.println "package $packageName"
   out.println ""

   if(hasId) {
      out.println "import com.baomidou.mybatisplus.annotation.IdType;"
      out.println "import com.baomidou.mybatisplus.annotation.TableId;"
   }

   out.println "import com.baomidou.mybatisplus.annotation.TableName;"
   out.println "import io.swagger.annotations.ApiModelProperty;"
   out.println "import lombok.Data;"
   out.println "import lombok.NoArgsConstructor;"
   out.println ""

   if(hasBigDecimal) {
      out.println "import java.math.BigDecimal;"
   }

   if(hasLocalDateTime) {
      out.println "import java.time.LocalDateTime;"
   }

   if(hasLocalDate) {
      out.println "import java.time.LocalDate;"
   }

   if(hasLocalTime) {
      out.println "import java.time.LocalTime;"
   }

   if(hasBigDecimal || hasLocalDateTime || hasLocalDate || hasLocalTime) {
      out.println ""
   }

   out.println "@Data"
   out.println "@NoArgsConstructor"
   out.println "@TableName(\"$tableName\")"
   out.println "public class $className {"

   for(int i = 0; i < fields.size(); i++) {
      def it = fields[i];
      if(it.annos != "") {
         out.println "   ${it.annos}"
      }

      if(it.name == "id") {
         out.println "   @TableId(type = IdType.ASSIGN_ID)"
         out.println "   @ApiModelProperty(\"主键ID\")"
      }
      else if(it.comment != null) {
         out.println "   @ApiModelProperty(\"${it.comment}\")"
      }

      out.println "   private ${it.type} ${it.name};"

      if(i != fields.size() - 1) {
         out.println ""
      }
   }

   out.println "}"
}

def calcFields(table) {
   DasUtil.getColumns(table).reduce([]) { fields, col ->
      def spec = Case.LOWER.apply(col.getDataType().getSpecification())
      def typeStr = typeMapping.find { p, t -> p.matcher(spec).find() }.value
      def name = javaName(col.getName(), false);

      dealDataType(typeStr);

      if(name) {
         hasId = true
      }

      fields += [[
                       name   : name,
                       type   : typeStr,
                       comment: col.getComment(),
                       annos  : ""]]
   }
}

def dealDataType(type) {
   if(type == "BigDecimal") {
      hasBigDecimal = true
   }

   if(type == "LocalDateTime") {
      hasLocalDateTime = true
   }

   if(type == "LocalDate") {
      hasLocalDate = true
   }

   if(type == "LocalTime") {
      hasLocalTime = true
   }
}

def getPackageName(dir) {
   return dir.toString().replaceAll("\\\\", ".").replaceAll("/", ".").replaceAll("^.*src(\\.main\\.java\\.)?", "") + ";"
}

def javaName(str, capitalize) {
   def s = com.intellij.psi.codeStyle.NameUtil.splitNameIntoWords(str)
         .collect { Case.LOWER.apply(it).capitalize() }
         .join("")
         .replaceAll(/[^\p{javaJavaIdentifierPart}[_]]/, "_")
   capitalize || s.length() == 1 ? s : Case.LOWER.apply(s[0]) + s[1..-1]
}
